﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace SmartMeterParamFrame.Connection
{
    /// <summary>
    /// udp 数据收发
    /// </summary>
    public class UDPClient
    {
        /// <summary>
        /// 全局的 侦超时时间
        /// </summary>
        public static int FrameTimeOut
        {
            get;
            set;
        }

        /// <summary>
        /// 全局的 字节超时时间
        /// </summary>
        public static int BitTimeOut
        {
            get;
            set;
        }

        /// <summary>
        /// 全局的 发送时待待时间
        /// </summary>
        public static int SendDelay
        {
            get;
            set;
        }

        /// <summary>
        /// 全局的误差板延时
        /// </summary>
        public static int WcbDelay
        {
            get;
            set;
        }

        /// <summary>
        /// 用来发送数据的 UDP
        /// </summary>
        private UdpClient m_DataClient;

        /// <summary>
        /// 接收的数据集合
        /// </summary>
        List<byte> m_RevItems = new List<byte>();

        /// <summary>
        /// 接收数据缓存
        /// </summary>
        byte[] m_RecvBuff = new byte[1024];

        /// <summary>
        /// 远程通信点
        /// </summary>
        private IPEndPoint m_RemotePoint = new IPEndPoint(IPAddress.Parse("193.168.18.1"), 10003);

        /// <summary>
        /// 是否已经成功初始化
        /// </summary>
        private bool m_IsInit = false;

        /// <summary>
        /// 设置 侦超时时间 
        /// </summary>
        protected int m_FrameTimeOut = 2000;

        /// <summary>
        /// 设置字节超时时间
        /// </summary>
        protected int m_BitTimeOut = 200;

        /// <summary>
        /// 发送前的等待时间
        /// </summary>
        protected int m_SendDelay = 30;

        /// <summary>
        /// 是否初始化
        /// </summary>
        public bool IsInit
        {
            get { return m_IsInit; }
        }

        /// <summary>
        /// 初始化通信模块
        /// </summary>
        /// <param name="Setting">通信参数（2400,e,8,1)</param>
        /// <param name="IP">远程主机IP地址</param>
        /// <param name="BindPort">本地数据端口</param>
        /// <param name="BindSetPort">本地设置端口</param>
        /// <param name="RemotePort">远程端口</param>
        /// <param name="frameTimeOut">侦间超时,如果不提供该值，则使用全局的FrameTimeOut</param>
        /// <param name="bitTimeOut">字节间超时,如果不提供该值，则使用全局的BitTimeOut</param>
        /// <param name="sendDelay">发送延迟,如果不提供该值，则使用全局的SendDelay</param>
        public void Init(string Setting, string IP, int BindPort, int BindSetPort, int RemotePort, int frameTimeOut = -1, int bitTimeOut = -1, int sendDelay = -1)
        {
            this.m_FrameTimeOut = frameTimeOut < 0 ? UDPClient.FrameTimeOut : frameTimeOut;
            this.m_BitTimeOut = bitTimeOut < 0 ? UDPClient.BitTimeOut : bitTimeOut;
            this.m_SendDelay = sendDelay < 0 ? UDPClient.SendDelay : sendDelay;

            m_RemotePoint.Address = IPAddress.Parse(IP);
            m_RemotePoint.Port = RemotePort;
            this.m_DataClient = CreateUdpClient(IP, RemotePort, BindPort);

            //初始化该端口所对应的rs485端口连接控制
            UdpClient initClient = CreateUdpClient(IP, RemotePort, BindSetPort);
            byte[] SendArr = ASCIIEncoding.ASCII.GetBytes(string.Format("init {0}", Setting.Replace(',', ' ')));
            m_IsInit = SendData(initClient, ref  SendArr, false);           //发送初始化参数
            initClient.Close();
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="vData">发送的二进制数据</param>
        /// <param name="IsReturn">是否需要返回</param>
        /// <returns></returns>
        public bool SendData(ref byte[] vData, bool IsReturn)
        {
            if (!m_IsInit) return false;
            //如果时间大于0，等待
            if (this.m_SendDelay > 0)
                Thread.Sleep(m_SendDelay);
            return SendData(this.m_DataClient, ref vData, IsReturn);
        }

        /// <summary>
        /// 关闭
        /// </summary>
        public void Close()
        {
            if (this.m_DataClient != null)
                this.m_DataClient.Close();
        }

        //创建UDP连接
        private UdpClient CreateUdpClient(string remoteIP, int remotePort, int localPort)
        {
            try
            {
                UdpClient client = new UdpClient(localPort);
                client.Connect(new IPEndPoint(IPAddress.Parse(remoteIP), remotePort));
                return client;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="Port">端口</param>
        /// <param name="vData">发送的二进制数据</param>
        /// <param name="IsReturn">是否需要返回</param>
        /// <returns></returns>
        private bool SendData(UdpClient client, ref Byte[] vData, bool IsReturn)
        {
            m_RevItems.Clear();
            client.Client.ReceiveTimeout = this.m_FrameTimeOut;

            //发送数据
            client.Send(vData, vData.Length);
            if (!IsReturn) // 如果不需要数据返回 
            {
                vData = this.m_RevItems.ToArray();
                return true;
            }

            int readed = ReadUDPSocketData(client, m_RecvBuff);
            if (readed > 0)
            {
                for (int i = 0; i < readed; i++)
                {
                    m_RevItems.Add(m_RecvBuff[i]);
                }
                client.Client.ReceiveTimeout = this.m_BitTimeOut;
                while (true)
                {
                    readed = ReadUDPSocketData(client, m_RecvBuff);
                    if (readed <= 0)
                        break;
                    else
                    {
                        for (int i = 0; i < readed; i++)
                        {
                            m_RevItems.Add(m_RecvBuff[i]);
                        }
                    }
                }
            }
            vData = m_RevItems.ToArray();
            return vData.Length > 0;
        }

        /// <summary>
        /// 从UDP读取数据
        /// </summary>
        /// <param name="s"></param>
        /// <param name="buff"></param>
        /// <returns></returns>
        private int ReadUDPSocketData(UdpClient s, byte[] buff)
        {
            try
            {
                return s.Client.Receive(buff);
            }
            catch (SocketException se)
            {
                return -1;
            }
        }
    }
}
